package com.example.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicLong;

/**
 * ID生成器服务
 * 基于时间戳+雪花算法生成10位ID
 */
@Service
public class IdGeneratorService {
    
    /**
     * 工作机器ID (0-31)
     */
    private final long workerId = 1L;
    
    /**
     * 数据中心ID (0-31)
     */
    private final long datacenterId = 1L;
    
    /**
     * 序列号 (0-4095)
     */
    private final AtomicLong sequence = new AtomicLong(0L);
    
    /**
     * 上次生成ID的时间戳
     */
    private volatile long lastTimestamp = -1L;
    
    /**
     * 开始时间戳 (2023-01-01)
     */
    private final long startTimestamp = 1672531200000L;
    
    /**
     * 各部分占用的位数
     */
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long sequenceBits = 12L;
    
    /**
     * 各部分的最大值
     */
    private final long maxWorkerId = ~(-1L << workerIdBits);
    private final long maxDatacenterId = ~(-1L << datacenterIdBits);
    private final long maxSequence = ~(-1L << sequenceBits);
    
    /**
     * 各部分向左的位移
     */
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    
    /**
     * 生成下一个10位ID
     * @return 10位唯一ID
     */
    public synchronized Long nextId() {
        long timestamp = timeGen();
        
        // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过，抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回退，拒绝生成ID");
        }
        
        // 如果是同一时间生成的，则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            long seq = sequence.incrementAndGet() & maxSequence;
            // 毫秒内序列溢出
            if (seq == 0) {
                // 阻塞到下一个毫秒，获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
                sequence.set(0L);
            }
        } else {
            // 时间戳改变，毫秒内序列重置
            sequence.set(0L);
        }
        
        // 上次生成ID的时间戳
        lastTimestamp = timestamp;
        
        // 移位并通过或运算拼到一起组成64位的ID
        long id = ((timestamp - startTimestamp) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift)
                | sequence.get();
        
        // 转换为10位数字
        return convertTo10Digits(id);
    }
    
    /**
     * 将生成的ID转换为10位数字
     * @param id 原始ID
     * @return 10位ID
     */
    private Long convertTo10Digits(long id) {
        // 取绝对值并确保是10位数
        long absId = Math.abs(id);
        
        // 如果超过10位，取模运算
        if (absId >= 10000000000L) {
            absId = absId % 10000000000L;
        }
        
        // 如果不足10位，补齐到10位
        if (absId < 1000000000L) {
            absId = 1000000000L + (absId % 1000000000L);
        }
        
        return absId;
    }
    
    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 当前时间戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    
    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    private long timeGen() {
        return System.currentTimeMillis();
    }
} 